const sherpa_onnx = require('sherpa-onnx');
const lamejs = require('lamejs');


function createWavHeader(dataLength, sampleRate, channels, bitDepth) {
    const header = Buffer.alloc(44);
    const byteRate = sampleRate * channels * bitDepth / 8;
    const blockAlign = channels * bitDepth / 8;

    header.write('RIFF', 0);
    header.writeUInt32LE(36 + dataLength, 4);
    header.write('WAVE', 8);
    header.write('fmt ', 12);
    header.writeUInt32LE(16, 16);
    header.writeUInt16LE(1, 20); // PCM
    header.writeUInt16LE(channels, 22);
    header.writeUInt32LE(sampleRate, 24);
    header.writeUInt32LE(byteRate, 28);
    header.writeUInt16LE(blockAlign, 32);
    header.writeUInt16LE(bitDepth, 34);
    header.write('data', 36);
    header.writeUInt32LE(dataLength, 40);

    return header;
}

function createOfflineTts() {
    // 创建 OfflineTts 实例
    const offlineTtsMatchaModelConfig = {
        acousticModel: './models/matcha-icefall-zh-baker/model-steps-3.onnx',
        vocoder: './models/vocos-22khz-univ.onnx',
        lexicon: './models/matcha-icefall-zh-baker/lexicon.txt',
        tokens: './models/matcha-icefall-zh-baker/tokens.txt',
        dictDir: './models/matcha-icefall-zh-baker/dict',
        noiseScale: 0.667,
        lengthScale: 1.0,
    };

    const offlineTtsModelConfig = {
        offlineTtsMatchaModelConfig: offlineTtsMatchaModelConfig,
        numThreads: 1,
        debug: 1,
        provider: 'cpu',
    };

    const offlineTtsConfig = {
        offlineTtsModelConfig: offlineTtsModelConfig,
        maxNumSentences: 1,
        ruleFsts:
            './models/matcha-icefall-zh-baker/phone.fst,./models/matcha-icefall-zh-baker/date.fst,./models/matcha-icefall-zh-baker/number.fst',
    };

    // const offlineTtsKokoroModelConfig = {
    //     model: './models/kokoro-multi-lang-v1_1/model.onnx',
    //     voices: './models/kokoro-multi-lang-v1_1/voices.bin',
    //     tokens: './models/kokoro-multi-lang-v1_1/tokens.txt',
    //     dataDir: './models/kokoro-multi-lang-v1_1/espeak-ng-data',
    //     dictDir: './models/kokoro-multi-lang-v1_1/dict',
    //     lexicon:
    //         './models/kokoro-multi-lang-v1_1/lexicon-us-en.txt,./models/kokoro-multi-lang-v1_1/lexicon-zh.txt',
    //     lengthScale: 1.0,
    // };
    // const offlineTtsModelConfig = {
    //     offlineTtsKokoroModelConfig: offlineTtsKokoroModelConfig,
    //     numThreads: 1,
    //     debug: 1,
    //     provider: 'cpu',
    // };

    // const offlineTtsConfig = {
    //     offlineTtsModelConfig: offlineTtsModelConfig,
    //     maxNumSentences: 1,
    // };

    return new sherpa_onnx.createOfflineTts(offlineTtsConfig);
}

function createOfflineRecognizer() {
    const config = {
        featConfig: {
            sampleRate: 16000,
            featureDim: 80,
        },
        modelConfig: {
            senseVoice: {
                model: './models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/model.int8.onnx',
                useInverseTextNormalization: 1,
            },
            tokens: './models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/tokens.txt',
            numThreads: 1,
            provider: 'cpu',
            debug: 1,
        }
    };

    return new sherpa_onnx.createOfflineRecognizer(config);
}

function audioToWavBuffer(audio) {
    const { samples, sampleRate } = audio;
    // 原始数据为 Float32，转换成 Int16 是必须的，因为标准 WAV PCM 是 16-bit
    const int16Array = new Int16Array(samples.length);
    for (let i = 0; i < samples.length; i++) {
        let s = samples[i];
        s = Math.max(-1, Math.min(1, s));
        int16Array[i] = s * 32767;
    }

    // 仅加 WAV 头部，其余保持原始 PCM 内容
    const wavHeader = createWavHeader(int16Array.length * 2, sampleRate, 1, 16);
    const wavBuffer = Buffer.concat([
        wavHeader,
        Buffer.from(int16Array.buffer)
    ]);
    return wavBuffer;
}

function audioToMp3Buffer(audio) {
    const { samples, sampleRate } = audio;
    // Float32 转 Int16（MP3 需要 Int16 PCM）
    const int16Array = new Int16Array(samples.length);
    for (let i = 0; i < samples.length; i++) {
        let s = samples[i];
        s = Math.max(-1, Math.min(1, s));
        int16Array[i] = s * 32767;
    }

    // 初始化 MP3 编码器（单声道，16bit）
    const mp3Encoder = new lamejs.Mp3Encoder(1, sampleRate, 128);
    const mp3Data = [];
    const chunkSize = 1152;

    for (let i = 0; i < int16Array.length; i += chunkSize) {
        const chunk = int16Array.subarray(i, i + chunkSize);
        const mp3buf = mp3Encoder.encodeBuffer(chunk);
        if (mp3buf.length > 0) {
            mp3Data.push(Buffer.from(mp3buf));
        }
    }

    // 完成编码
    const mp3End = mp3Encoder.flush();
    if (mp3End.length > 0) {
        mp3Data.push(Buffer.from(mp3End));
    }

    const mp3Buffer = Buffer.concat(mp3Data);
    return mp3Buffer;
}

module.exports = {
    audioToWavBuffer,
    audioToMp3Buffer,
    tts: createOfflineTts(),
    recognizer: createOfflineRecognizer()
};